package cn.leshua.util;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.leshua.exception.BizConstants;
import cn.leshua.exception.BizRuntimeException;

/**
 * @author eden
 * @date 2018/5/21 下午3:36
 * @desc
 */
public class HttpClientHelper {

	private static Logger log = LoggerFactory.getLogger(HttpClientHelper.class);

	public static final String ACCEPT_TYPE_JSON = "application/json";
	public static final String ACCEPT_TYPE_HTML = "text/html";
	public static final String ACCEPT_TYPE_XML = "application/xml";
	public static final String ACCEPT_TYPE_X_WWW = "application/x-www-form-urlencoded";
	public static final int DEFAULT_SO_TIMEOUT = 30000;
	public static final int DEFAULT_CONN_TIMEOUT = 6000;
	public static final int DEFAULT_CONN_REQ_TIMEOUT = 5000;

	private static PoolingHttpClientConnectionManager cm;

	private RequestConfig requestConfig;
	
	static {
		cm = new PoolingHttpClientConnectionManager();
		cm.setMaxTotal(10000);
		cm.setDefaultMaxPerRoute(500);
	}

	@PostConstruct
	public void init() {
		// 设置默认http状态参数
		requestConfig = RequestConfig.custom().setSocketTimeout(DEFAULT_SO_TIMEOUT).setConnectTimeout(DEFAULT_CONN_TIMEOUT)
				.setConnectionRequestTimeout(DEFAULT_CONN_REQ_TIMEOUT).build();
	}
	
	/**
	 * 关闭HttpClient连接池
	 */
	@PreDestroy
	public void destory() {
		if (cm != null) {
			log.info("关闭HttpClient线程池");
			cm.shutdown();
		}
	}

	/**
	 * get请求默认格式为html，编码为utf-8
	 * @param url
	 * @return
	 */
	public String get(String url) {
		return this.get(url, "text/html", null, BizConstants.CHARSET_UTF8);
	}

	/**
	 * get请求,指定格式，编码为utf-8
	 * @param url
	 * @param acceptType
	 * @return
	 */
	public String get(String url, String acceptType) {
		return this.get(url, acceptType, null, BizConstants.CHARSET_UTF8);
	}
	
	/**
	 * get请求,指定格式，编码为utf-8
	 * @param url
	 * @param acceptType
	 * @return
	 */
	public String get(String url, Map<String, String> params, String acceptType) {
		StringBuilder paramSB = new StringBuilder();
		if(params != null){
			for (Entry<String, String> entry : params.entrySet()) {
				paramSB.append("&").append(entry.getKey()).append("=").append(entry.getValue());
			}
		}
		if (paramSB.length() > 0) {
			url = url + "?" + paramSB.substring(1);
		}
		return this.get(url, acceptType);
	}

	/**
	 * get请求,指定格式，编码为utf-8，需要授权
	 * @param url
	 * @param acceptType
	 * @return
	 */
	public String get(String url, String acceptType, String auth) {
		return this.get(url, acceptType, auth, BizConstants.CHARSET_UTF8);
	}

	/**
	 * get请求,指定格式，指定编码，需要授权
	 * @param url
	 * @param acceptType
	 * @return
	 */
	public String get(String url, String acceptType, String auth, String charset) {
		String result = null;
		CloseableHttpClient httpClient = null;
		CloseableHttpResponse response = null;
		HttpEntity entity = null;
		try {
			httpClient = HttpClients.custom().setConnectionManager(cm).setConnectionManagerShared(true).build();
			HttpGet httpGet = new HttpGet(url);
			httpGet.setConfig(requestConfig);
			httpGet.setHeader("Accept", acceptType);
			httpGet.setHeader("Content-Type", acceptType + ";charset=" + charset);
			if (auth != null) {
				httpGet.setHeader("Authorization", auth);
			}
			// 执行客户端请求
			response = httpClient.execute(httpGet);
			entity = response.getEntity();
			if (entity != null) {
				result = EntityUtils.toString(entity, charset);
			}
		} catch (Exception e) {
			throw new BizRuntimeException(null, e);
		} finally {
			// 关闭连接
			if (entity != null) {
				EntityUtils.consumeQuietly(entity);
			}
			if (response != null) {
				try {
					response.close();
				} catch (IOException e) {
					log.error("关闭HttpResponse出错，错误信息：" + e.getMessage(), e);
				}
			}
			if (httpClient != null) {
				try {
					httpClient.close();
				} catch (IOException e) {
					log.error("关闭HttpClient出错，错误信息：" + e.getMessage(), e);
				}
			}
		}
		return result;
	}

	/**
	 * post请求，默认utf-8编码，期望接收text/html
	 * @param url
	 * @param params
	 * @param acceptType
	 * @return
	 */
	public String post(String url, String params) {
		return this.post(url, params, ACCEPT_TYPE_HTML, null, BizConstants.CHARSET_UTF8);
	}
	
	/**
	 * post请求，默认utf-8编码，指定格式
	 * @param url
	 * @param params
	 * @param acceptType
	 * @return
	 */
	public String post(String url, String params, String acceptType) {
		return this.post(url, params, acceptType, null, BizConstants.CHARSET_UTF8);
	}

	/**
	 * post请求，默认utf-8编码，指定格式
	 * @param url
	 * @param params
	 * @param acceptType
	 * @return
	 */
	public String post(String url, String params, String acceptType, int socketTimeOut , int connectTimeOut) {
		requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeOut).setConnectTimeout(connectTimeOut)
				.setConnectionRequestTimeout(DEFAULT_CONN_REQ_TIMEOUT).build();
		return this.post(url, params, acceptType, null, BizConstants.CHARSET_UTF8);
	}

	/**
	 * post请求，默认utf-8编码，指定格式，需要授权
	 * @param url
	 * @param params
	 * @param acceptType
	 * @param auth
	 * @return
	 */
	public String post(String url, String params, String acceptType, String auth) {
		return this.post(url, params, acceptType, auth, BizConstants.CHARSET_UTF8);
	}

	/**
	 *  post请求，指定编码，指定格式，需要授权
	 * @param url
	 * @param params
	 * @param acceptType
	 * @param auth
	 * @param charset
	 * @return
	 */
	public String post(String url, String params, String acceptType, String auth, String charset) {
		String result = null;
		CloseableHttpClient httpClient = null;
		CloseableHttpResponse response = null;
		HttpEntity entity = null;
		try {
			httpClient = HttpClients.custom().setConnectionManager(cm).setConnectionManagerShared(true).build();
			HttpPost httpPost = new HttpPost(url);
			httpPost.setConfig(requestConfig);
			httpPost.setHeader("Accept", acceptType);
			httpPost.setHeader("Content-Type", acceptType + ";charset=" + charset);
			if (auth != null) {
				httpPost.setHeader("Authorization", auth);
			}
			BasicHttpEntity requestBody = new BasicHttpEntity();
			byte[] content = params.getBytes(charset);
			requestBody.setContent(new ByteArrayInputStream(content));
			requestBody.setContentLength(content.length);
			httpPost.setEntity(requestBody);
			if (StringUtils.startsWithIgnoreCase(url, "https")) {
				trustAllHosts();
			}
			// 执行客户端请求
			response = httpClient.execute(httpPost);
			entity = response.getEntity();
			if (entity != null) {
				result = EntityUtils.toString(entity, charset);
			}
		} catch (Exception e) {
			throw new BizRuntimeException(null, e);
		} finally {
			// 关闭连接
			if (entity != null) {
				EntityUtils.consumeQuietly(entity);
			}
			if (response != null) {
				try {
					response.close();
				} catch (IOException e) {
					log.error("关闭HttpResponse出错，错误信息：" + e.getMessage(), e);
				}
			}
			if (httpClient != null) {
				try {
					httpClient.close();
				} catch (IOException e) {
					log.error("关闭HttpClient出错，错误信息：" + e.getMessage(), e);
				}
			}
		}
		return result;
	}
	
	/**
	 * post请求，默认utf-8编码，期望接收text/html
	 * @param url
	 * @param params
	 * @return
	 */
	public String postForm(String url, Map<String, String> formParams) {
		return this.postForm(url, formParams, ACCEPT_TYPE_HTML, null, BizConstants.CHARSET_UTF8);
	}
	
	/**
	 * post请求，默认utf-8编码，期望接收text/html
	 * @param url
	 * @param formParams
	 * @param socketTimeOut
	 * @param connectTimeOut
	 * @return
	 */
	public String postForm(String url, Map<String, String> formParams, int socketTimeOut , int connectTimeOut) {
		requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeOut).setConnectTimeout(connectTimeOut)
				.setConnectionRequestTimeout(DEFAULT_CONN_REQ_TIMEOUT).build();
		return this.postForm(url, formParams, ACCEPT_TYPE_HTML, null, BizConstants.CHARSET_UTF8);
	}
	
	/**
	 * post请求，默认utf-8编码，指定格式
	 * @param url
	 * @param params
	 * @param acceptType
	 * @return
	 */
	public String postForm(String url, Map<String, String> formParams, String acceptType) {
		return this.postForm(url, formParams, acceptType, null, BizConstants.CHARSET_UTF8);
	}

	/**
	 * post请求，默认utf-8编码，指定格式，需要授权
	 * @param url
	 * @param params
	 * @param acceptType
	 * @param auth
	 * @return
	 */
	public String postForm(String url, Map<String, String> formParams, String acceptType, String auth) {
		return this.postForm(url, formParams, acceptType, auth, BizConstants.CHARSET_UTF8);
	}
	
	/**
	 * post请求，指定编码，指定格式，需要授权
	 * @param url
	 * @param formParams
	 * @param acceptType
	 * @param auth
	 * @param charset
	 * @return
	 */
	public String postForm(String url, Map<String, String> formParams, String acceptType, String auth, String charset) {
		List<NameValuePair> urlParameters = new ArrayList<NameValuePair>();
		if (formParams != null) {
			for (Entry<String, String> entry : formParams.entrySet()) {
				urlParameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
			}
		}
		boolean isSucc = false;
		HttpEntity requestEntity = null;
		try {
			requestEntity = new UrlEncodedFormEntity(urlParameters, charset);
			isSucc = true;
		} catch (UnsupportedEncodingException e) {
			log.error(e.getMessage(), e);
		}
		return isSucc ? this.postForm(url, requestEntity, acceptType, auth, charset) : null;
	}

	/**
	 * Trust every server - dont check for any certificate
	 */
	public static void trustAllHosts() {
		// Create a trust manager that does not validate certificate chains
		TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
			public java.security.cert.X509Certificate[] getAcceptedIssuers() {
				return new java.security.cert.X509Certificate[] {};
			}

			public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
			}

			public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
			}
		} };

		try {
			SSLContext sc = SSLContext.getInstance("TLS");
			sc.init(null, trustAllCerts, new java.security.SecureRandom());
			HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
	}
	
	/**
	 * post请求，指定编码，指定格式，需要授权
	 * @param url
	 * @param requestEntity
	 * @param acceptType
	 * @param auth
	 * @param charset
	 * @return
	 */
	private String postForm(String url, HttpEntity requestEntity, String acceptType, String auth, String charset) {
		String result = null;
		CloseableHttpClient httpClient = null;
		CloseableHttpResponse response = null;
		HttpEntity entity = null;
		try {
			httpClient = HttpClients.custom().setConnectionManager(cm).setConnectionManagerShared(true).build();
			HttpPost httpPost = new HttpPost(url);
			httpPost.setConfig(requestConfig);
			httpPost.setHeader("Accept", acceptType);
			httpPost.setHeader("Content-Type","application/x-www-form-urlencoded;charset=" + charset);
			if (auth != null) {
				httpPost.setHeader("Authorization", auth);
			}
			httpPost.setEntity(requestEntity);
			if (StringUtils.startsWithIgnoreCase(url, "https")) {
				trustAllHosts();
			}
			// 执行客户端请求
			response = httpClient.execute(httpPost);
			entity = response.getEntity();
			if (entity != null) {
				result = EntityUtils.toString(entity, charset);
			}
		} catch (Exception e) {
			throw new BizRuntimeException(null, e);
		} finally {
			// 关闭连接
			if (entity != null) {
				EntityUtils.consumeQuietly(entity);
			}
			if (response != null) {
				try {
					response.close();
				} catch (IOException e) {
					log.error("关闭HttpResponse出错，错误信息：" + e.getMessage(), e);
				}
			}
			if (httpClient != null) {
				try {
					httpClient.close();
				} catch (IOException e) {
					log.error("关闭HttpClient出错，错误信息：" + e.getMessage(), e);
				}
			}
		}
		return result;
	}
	
	/**
	 * 
	 * @param url
	 * @param acceptType
	 * @param paramMap
	 * @param fileMap
	 * @param charset
	 * @return
	 */
	public String postWithMultipartEntity(String url, String acceptType, Map<String, String> paramMap, Map<String, File> fileMap,
			String charset) {
		String result = null;
		CloseableHttpClient httpClient = null;
		CloseableHttpResponse response = null;
		HttpEntity entity = null;
		try {
			httpClient = HttpClients.custom().setConnectionManager(cm).setConnectionManagerShared(true).build();
			HttpPost httpPost = new HttpPost(url);
			httpPost.setConfig(requestConfig);
			httpPost.setHeader("Accept", acceptType);

			MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
			multipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
			multipartEntityBuilder.setCharset(Charset.forName(charset));
			// 字符串参数
			if (paramMap != null) {
				for (Entry<String, String> entry : paramMap.entrySet()) {
					StringBody strBody = new StringBody(entry.getValue(), ContentType.APPLICATION_JSON);
					multipartEntityBuilder = multipartEntityBuilder.addPart(entry.getKey(), strBody);
				}
			}
			// 文件参数
			if (fileMap != null) {
				for (Entry<String, File> entry : fileMap.entrySet()) {
					FileBody fileBody = new FileBody(entry.getValue());
					multipartEntityBuilder = multipartEntityBuilder.addPart(entry.getKey(), fileBody);
				}
			}
			HttpEntity reqEntity = multipartEntityBuilder.build();
			httpPost.setEntity(reqEntity);

			if (StringUtils.startsWithIgnoreCase(url, "https")) {
				trustAllHosts();
			}
			// 执行客户端请求
			response = httpClient.execute(httpPost);
			entity = response.getEntity();
			if (entity != null) {
				result = EntityUtils.toString(entity, charset);
			}
		} catch (Exception e) {
			throw new BizRuntimeException(null, e);
		} finally {
			// 关闭连接
			if (entity != null) {
				EntityUtils.consumeQuietly(entity);
			}
			if (response != null) {
				try {
					response.close();
				} catch (IOException e) {
					log.error("关闭HttpResponse出错，错误信息：" + e.getMessage(), e);
				}
			}
			if (httpClient != null) {
				try {
					httpClient.close();
				} catch (IOException e) {
					log.error("关闭HttpClient出错，错误信息：" + e.getMessage(), e);
				}
			}
		}
		return result;
	}
	
}
